
#include "stdafx.h"
#include "Ball_3d.h"
#include "rendering.h"
#include "Control.h"
using namespace std;
extern Control lab;
static float viewx = 0.5*L, viewy = 0.6*L, viewz = 5;
static float viewlx = 0, viewly = 0, viewlz = -1;
static const float cameraMoveSpeed = 0.5;
static const float cameraRotateSpeed = 0.05;
static float viewAngle = 0, viewAngleZ = 0;

//@tc : the Control to be simulated and rendered. haven't figured a way to pack it.

extern const double GRIDSIZE ;
void rendering(int argc, char** argv)
{
	//@tc : set window
	glutInit(&argc, argv);

	//@tc : set lighting
	const GLfloat light_ambient[]  = { 0.0f, 0.0f, 0.0f, 1.0f };
	const GLfloat light_diffuse[]  = { 1.0f, 1.0f, 1.0f, 1.0f };
	const GLfloat light_specular[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	const GLfloat light_position[] = { 0.0f, L*1.0f, L*1.0f, 0.0f };
	const GLfloat mat_ambient[]    = { 0.7f, 0.7f, 0.7f, 1.0f };
	const GLfloat mat_diffuse[]    = { 0.8f, 0.8f, 0.8f, 1.0f };
	const GLfloat mat_specular[]   = { 1.0f, 1.0f, 1.0f, 1.0f };
	const GLfloat high_shininess[] = { 100.0f };
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(300,50);
	glutInitWindowSize(1024, 1024);

	//@tc : the caption of the window.
	glutCreateWindow("Light Balls");

	//@tc : displays the scene.
	glutDisplayFunc(renderScene);
	glutReshapeFunc(changeAspectRatio);
	glutIdleFunc(renderScene);
	glutSpecialFunc( specialKeys );
	glutKeyboardFunc( normalKeys );
	glutMouseFunc( MouseKey );
	glutMotionFunc( MouseMove );
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LESS);

	glEnable(GL_LIGHT0);
	glEnable(GL_NORMALIZE);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);
	glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);

	glMaterialfv(GL_FRONT, GL_AMBIENT,   mat_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE,   mat_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR,  mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);
	glutMainLoop();
}
void changeAspectRatio(int w, int h)
{
	if ( h == 0 ) h = 1;
	float ratio = 1.0 * w / h;

	//@tc : set projection mode
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();

	//@tc : set the view range. w h means the whole window.
	glViewport(0, 0, w, h);


	//@tc : set aspect ratio, whenever the windows changes size, the object ramains the original aspect ratio
	gluPerspective(45, ratio, 0.1, 1000);


	//@tc : set view model
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx ,viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void renderScene()
{

	static int frame = 0;
	static int timeBase = 0;
	int time = glutGet( GLUT_ELAPSED_TIME );
	Control* first = &lab;
	//@tc : draws the scene each 25ms. If the computation is done is less than that.
	if (time - timeBase > 15)
	{
		first->simulate();
		cout<<"\r"<<"Approximation Quality : "<<first->analysis->spinWaveApproximationQuality( first->flock);
		//cout<<"\r"<<"FPS : "<<frame * 1000.0 / (time - timeBase) ;
		//cout<<"\r"<<first->analysis->getOrderParameter( first->flock);
		frame = 0;
		timeBase = time;
	}
	frame++;

	//@tc : draws the cubic
	//@tc : Front Back + Four Lines
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	//@tc :enable dash line
	glEnable(GL_LINE_STIPPLE);
	glColor3f(L, L, L);
	glLineWidth( 0.2 );
	glLineStipple(L,0xFFFF);
	glBegin( GL_LINE_LOOP );
	glVertex3f(0, 0, -L);
	glVertex3f(L, 0, -L);
	glVertex3f(L, L, -L);
	glVertex3f(0, L, -L);
	glEnd();

	glLineWidth( 0.2 );
	glLineStipple(L, 0x0F0F);
	glBegin( GL_LINE_LOOP );
	glVertex3f(0, 0, -2*L);
	glVertex3f(L, 0, -2*L);
	glVertex3f(L, L, -2*L);
	glVertex3f(0, L, -2*L);
	glEnd();

	glBegin( GL_LINES );
	glVertex3f(0, L, -L);
	glVertex3f(0, L, -2*L);
	glEnd();

	glBegin( GL_LINES );
	glVertex3f(0, 0, -L);
	glVertex3f(0, 0, -2*L);
	glEnd();

	glBegin( GL_LINES );
	glVertex3f(L, 0, -L);
	glVertex3f(L, 0, -2*L);
	glEnd();

	glBegin( GL_LINES );
	glVertex3f(L, L, -L);
	glVertex3f(L, L, -2*L);
	glEnd();
	GLuint ballDL = glGenLists( 1 );
	glNewList( ballDL, GL_COMPILE );
	glutSolidSphere(BALL_RADIUS, 20, 20);
	glEndList();
	glColor3f(1, 0, 0);
	//for(int i = 0; i < 5; i++)
	//	for(int j = 0; j < 5; j++)
	//		for(int k = 0; k < 5; k++)
	//		{
	//			glPushMatrix();
	//			glTranslatef(0.2*i, 0.2*j, -1 - 0.2*k);
	//			glCallList( ballDL );
	//			glPopMatrix();
	//		}

	for(int i = 0; i < first->flock->nBirds; i++)
	{
		//@tc : set different colors to idenfity different clusters.
		switch (first->flock->color[i])
		{
		case 0 : glColor3f(1,0,0);break;
		case 1 : glColor3f(0,1,0);break;
		case 2 : glColor3f(0,0,1);break;
		case 3 : glColor3f(1,1,0);break;
		case 4 : glColor3f(1,0,1);break;
		case 5 : glColor3f(0,1,1);break;
		case 6 : glColor3f(1,1,1);break;
		}
		float x = (float)(first->flock->bird[i]).X.x;
		float y = (float)(first->flock->bird[i]).X.y;
		float z = (float)(first->flock->bird[i]).X.z - 2*L;
		glPushMatrix();
		glTranslatef(x, y, z);
		glCallList( ballDL );
		glPopMatrix();
	}
	glutPostRedisplay();
	glutSwapBuffers();
}

void cameraMoveLeft (int dx)
{
	viewx = viewx + cameraMoveSpeed * dx;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraMoveUp( int dy )
{
	viewy = viewy + cameraMoveSpeed * dy;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraMoveFront (int dz)
{
	viewz = viewz + cameraMoveSpeed * dz;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraMoveForward (int dd)
{
    viewx += dd*cameraMoveSpeed*viewlx;
    viewy += dd*cameraMoveSpeed*viewly;
    viewz += dd*cameraMoveSpeed*viewlz;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraMovePlayerLeft(int dl)
{
    double vx = -viewlz;
    double vy =  viewlx;
    double ab = sqrt(vx*vx+vy*vy);
    vx /= ab;
    vy /= ab;
    viewx += dl*cameraMoveSpeed*vx;
    viewz += dl*cameraMoveSpeed*vy;
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraRotateLeft(double dx)
{
	viewAngle += cameraRotateSpeed * dx;
	viewlx =  sin( viewAngle )*cos(viewAngleZ);
	viewlz = -cos( viewAngle )*cos(viewAngleZ);
	viewly = sin(viewAngleZ);
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void cameraRotateUp(double dz)
{
	viewAngleZ += cameraRotateSpeed * dz;
	if (abs(viewAngleZ) >= pi/2) {
	    viewAngleZ -= cameraRotateSpeed * dz;
	}
	viewlx =  sin( viewAngle )*cos(viewAngleZ);
	viewlz = -cos( viewAngle )*cos(viewAngleZ);
	viewly = sin(viewAngleZ);
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	gluLookAt(viewx, viewy, viewz, viewx + viewlx, viewy + viewly, viewz + viewlz, 0, 1, 0);
}

void specialKeys(int key, int x, int y)
{
	switch ( key )
	{
    // coordinate movement
	case GLUT_KEY_LEFT : {
        int specialKey = glutGetModifiers();
        if (specialKey == GLUT_ACTIVE_CTRL )
            cameraRotateLeft( -1 );
        else
            cameraMoveLeft ( -1 );
        break;
    }
	case GLUT_KEY_RIGHT : {
		int specialKey = glutGetModifiers();
		if (specialKey == GLUT_ACTIVE_CTRL )
			cameraRotateLeft( 1 );
		else
			cameraMoveLeft ( 1 );
		break;
    }
	case GLUT_KEY_UP : cameraMoveUp( 1 ); break;
	case GLUT_KEY_DOWN : cameraMoveUp( -1 ); break;
	case GLUT_KEY_PAGE_UP : cameraMoveFront( -1 ); break;
	case GLUT_KEY_PAGE_DOWN : cameraMoveFront( 1 ); break;
	}
}

void normalKeys(unsigned char key, int x, int y)
{
    switch ( key ) {
    case 27 : exit(0); break;
	// player view movement
	case 'w' : cameraMoveForward(  1 ); break;     // forward
	case 's' : cameraMoveForward( -1 ); break;     // backward
	case 'a' : cameraMovePlayerLeft( -1 ); break;  // move left
	case 'd' : cameraMovePlayerLeft(  1 ); break;  // move right
	case 'q' : cameraRotateUp(  1 ); break;        // turn up
	case 'z' : cameraRotateUp( -1 ); break;        // turn down
	case 'e' : cameraMoveUp(  1 ); break;          // move up
	case 'c' : cameraMoveUp( -1 ); break;          // move down
    }
}

static bool b_leftkey = false;
static int last_x, last_y;

void MouseKey(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        b_leftkey = true;
        last_x=x;
        last_y=y;
    }
    if (button == GLUT_RIGHT_BUTTON && state == GLUT_UP) {
        b_leftkey = false;
    }
}

void MouseMove(int x, int y)
{
    // rotate by mouse
    if (b_leftkey) {
        cameraRotateLeft(0.1*(x-last_x));
        cameraRotateUp(0.1*(last_y-y));
    }
    last_x=x;
    last_y=y;
}
